//
//  BaseTypeTestCase.swift
//  MKCodableTests
//
//  Created by 苏尚进 on 2020/5/14.
//  Copyright © 2020 moon. All rights reserved.
//

import Foundation
import XCTest
import MKCodable

enum E : Int , Codable {
    case e0,e1,e2
}

struct S<T>{
    var value:T?
    var key:String = "key"
    var customCoding:((inout S<T>,Coder)->Void)?
    init(_ value:T? , _ key:String = "key") {
        self.value = value
        self.key = key
    }
    init(_ value:T?,_ customCoding:((inout S<T>,Coder)->Void)?) {
        self.value = value
        self.customCoding = customCoding
    }
}
extension S : Equatable where T:Equatable {
    static func == (lhs: S<T>, rhs: S<T>) -> Bool {
        return lhs.value == rhs.value
    }
}
extension S : Codable where T:Codable {
    static func initObject(from coder: Coder) throws -> S<T> {
        var obj = Self.init(nil)
        try obj.code(coder: coder)
        if obj.value == nil {
            obj.value <= coder[[]]
        }
        return obj
    }
    mutating func code(coder: Coder) throws {
        if let f = customCoding {
            f(&self,coder)
        } else {
            self.value .= coder[key]
        }
    }
}

class BaseTypeTestCase : XCTestCase {
    func _setValue(_ value:inout NSNumber){
        value = NSNumber.init(integerLiteral: 10)
    }
    func _setValue2(_ value:inout AnyObject){
        value = NSNumber.init(integerLiteral: 11)
    }
    func testInit_Int1() { _testInitSuccess(Int(1),"{\"key\":1}") }
    func testInit_Int2() { _testInitSuccess(Int(1),"{\"key\":\"1\"}") }
    func testInit_Int3() { _testInitSuccess(Int(1),"{\"key\":\"1.0\"}") }
    func testInit_Int4() { _testInitSuccess(Int(1),"{\"key\":1.0}") }
    func testInit_Int5() { _testInitSuccess(Int(1),"{\"key\":1.1}",false) }
    func testInit_Int6() { _testInitSuccess(Int(1),"{\"key\":\"abc\"}",false) }
    func testInit_Int7() { _testInitSuccess(Int(0xff),"{\"key\":\"0xff\"}") }
    func testInit_Int8() { _testInitSuccess([1,2,3],"{\"key\":[1,2,3]}") }
    func testInit_Int9() { _testInitSuccess([1,2,3],"{\"key\":[1,\"0x02\",\"3\"]}") }
    func testInit_Int10() { _testInitArraySuccess([1,2,3],"[{\"key\":1},{\"key\":2},{\"key\":3}]") }
    func testInit_Int11() { _testInitArraySuccess([1,2,3],"[1,\"0x02\",\"3\"]") }
    
    func testDecode_Int1() { var s = S<Int>(0);_testDecodeSuccess(&s,Int(1),"{\"key\":1}") }
    func testDecode_Int1_2() { var s = S<Int>(0){$0.value .= $1["key"]};_testDecodeSuccess(&s,Int(1),"{\"key\":1}") }
    func testDecode_Int2() { var s = S<Int>(0,"nest.key");_testDecodeSuccess(&s,Int(1),"{\"nest\":{\"key\":1}}") }
    func testDecode_Int3() { var s = S<Int>(0,"nest.key");_testDecodeSuccess(&s,Int(1),"{\"nest.key\":1}",false) }
    func testDecode_Int4() { var s = S<Int>(0){$0.value .= $1["nest.key",false]};_testDecodeSuccess(&s,Int(1),"{\"nest.key\":1}") }
    func testDecode_Int5() { var s = S<Int>(0){$0.value .= $1["nest_key",true,"_"]};_testDecodeSuccess(&s,Int(1),"{\"nest\":{\"key\":1}}") }
    func testDecode_Int6() { var s = S<Int>(0,"nest.1");_testDecodeSuccess(&s,Int(2),"{\"nest\":[1,2,3]}") }
    func testDecode_Int7() { var s = S<Int>(0){$0.value .= $1[[StringCodingKey("nest"),StringCodingKey("1")]]};_testDecodeSuccess(&s,Int(2),"{\"nest\":{\"1\":2}}")}
    func testDecode_Int8() { var s = [S<Int>(0),S<Int>(0){$0.value .= $1[[]]}];_testDecodeArraySuccess(&s,[1,2],"[{\"key\":1},2]")}
    func testDecode_Int9() { var s = [S<Int>(0){$0.value .= $1[[]]}];_testDecodeArraySuccess(&s,[2],"[2]")}
    
    func testEncode_Int1() { _testEncodeSuccess(S<Int>(1),"{\"key\":1}") }
    func testEncode_Int1_1() { _testEncodeSuccess(S<Int>(1){
        var c:UnkeyedCodingContainer = try! $1["key"].decode()
        try? c.encode($0.value!)
    },"{\"key\":[1]}") }
    func testEncode_Int2() { _testEncodeSuccess(S<Int>(1,"nest.key"),"{\"nest\":{\"key\":1}}") }
    func testEncode_Int3() { _testEncodeSuccess(S<Int>(1,"nest.0"),"{\"nest\":[1]}") }
    func testEncode_Int3_1() { _testEncodeSuccess(S<Int>(1,"nest.1"),"{\"nest\":[1]}",false) }
    func testEncode_Int4() { _testEncodeSuccess([S<Int>(1),S<Int>(2)],"[{\"key\":1},{\"key\":2}]") }
    func testEncode_Int5() { _testEncodeSuccess([S<Int>(1){$0.value .= $1[[]]},S<Int>(2,"nest.key"),S<Int>(3),S<Int>(4,"nest.0")],"[1,{\"nest\":{\"key\":2}},{\"key\":3},{\"nest\":[4]}]") }
    func testEncode_Int6() { _testEncodeSuccess(S<Int>(Int.max),"{\"key\":\(Int.max.description)}") }
    func testEncode_Int8() { _testEncodeSuccess(S<Int>(nil){$0.value .= $1["key"][true]},"{\"key\":null}") }
    func testEncode_Int9() { _testEncodeSuccess([S<Int>(1),S<Int>(nil){$0.value .= $1["key"]},S<Int>(3)],"[{\"key\":1},{\"key\":3}]") }
    func testEncode_Int10() { _testEncodeSuccess([S<Int>(1),S<Int>(nil){$0.value .= $1[[]][true]},S<Int>(3){$0.value .= $1[[]]}],"[{\"key\":1},null,3]") }
    func testEncode_Int11() { _testEncodeSuccess([S<Int>(3){$0.value .= $1[[]]}],"[3]") }
    func testEncode_Int12() { _testEncodeSuccess(S<[Int]>([1,2]){
        try? $1.encode(contentsOf: $0.value!,[StringCodingKey("key")])
        },"{\"key\":[1,2]}") }
    func testEncode_Int13() { _testEncodeSuccess(S<[Int]>([1,2]),"{\"key\":[1,2]}") }
    
    
    func testInit_NSNumber1() { _testInitSuccess(NSNumber(value: 2),"{\"key\":2}") }
    func testDecode_NSNumber1() { var s = S<NSNumber>(NSNumber(value: 0));_testDecodeSuccess(&s,NSNumber(value: 2),"{\"key\":\"2\"}") }
    
    func testEncode_NSNumber1() { _testEncodeSuccess(S<NSNumber>(NSNumber(value: 2)), "{\"key\":\"2\"}") }
    
    func testEncode_Dict1() { _testEncodeSuccess(S<[String:Int]>(["k1":Int(1)]), "{\"key\":{\"k1\":1}}") }
    func testInit_Dict1() {  _testInitSuccess(["k1":Int(1),"k2":Int(2)],"{\"key\":{\"k1\":1,\"k2\":2}}") }
    func testDecode_Dict1() { var s = S<[String:Int]>(nil);_testDecodeSuccess(&s, ["k1":1,"k2":2], "{\"key\":{\"k1\":1,\"k2\":2}}") }
    
    func testInit_UIColor1() { _testInitSuccess(S<UIColor>(UIColor.red), "{\"key\":\"#ff0000\"}") }
    func testInit_UIColor2() { _testInitSuccess(S<UIColor>(UIColor.green), "{\"key\":\"#00ff00\"}") }
    func testInit_UIColor3() { _testInitSuccess(S<UIColor>(UIColor.blue), "{\"key\":\"#0000ff\"}") }
    func testInit_UIColor4() { _testInitSuccess(S<UIColor>(UIColor.red), "{\"key\":\"red\"}") }
    func testInit_UIColor5() { _testInitSuccess(S<UIColor>(UIColor.red), "{\"key\":\"#F00\"}") }
    func testInit_UIColor6() { _testInitSuccess(S<UIColor>(UIColor.red), "{\"key\":\"rgb(255,0,0)\"}") }
    func testInit_UIColor7() { _testInitSuccess(S<UIColor>(UIColor.red), "{\"key\":\"rgba(255,0,0,1)\"}") }
    func testDecode_UIColor1() { var s = S<UIColor>(nil);_testDecodeSuccess(&s, UIColor.red, "{\"key\":\"red\"}") }
    func testEncode_UIColor1() { _testEncodeSuccess(S<UIColor>(UIColor.red), "{\"key\":\"#ff0000\"}")}
    func testEncode_UIColor2() { _testEncodeSuccess(S<UIColor>(UIColor.red){$0.value .= ($1["key"],UIColorCodingConvertor(.rgb))}, "{\"key\":\"rgb(255,0,0)\"}")}
    func testEncode_UIColor3() { _testEncodeSuccess(S<UIColor>(UIColor.red){$0.value .= ($1["key"],UIColorCodingConvertor(.rgba))}, "{\"key\":\"rgba(255,0,0,1.0)\"}")}
    func testEncode_UIColor4() { _testEncodeSuccess(S<UIColor>(UIColor.init(hue: 0.5, saturation: 0.5, brightness: 0.5, alpha: 1)){$0.value .= ($1["key"],UIColorCodingConvertor(.hsla))}, "{\"key\":\"hsla(127,50%,50%,1.0)\"}")}
    func testEncode_UIColor5() { _testEncodeSuccess(S<UIColor>(UIColor.init(hue: 0.5, saturation: 0.5, brightness: 0.5, alpha: 1)){$0.value .= ($1["key"],UIColorCodingConvertor(.hsl))}, "{\"key\":\"hsl(127,50%,50%)\"}")}
    
    func testDecode_Enum1() { var s = S<E>(.e0);_testDecodeSuccess(&s, .e1, "{\"key\":1}") }
    func testDecode_Enum2() { var s = S<E>(.e0){$0.value .= ($1["key"],EnumCodingConvertor<String,E>(["e0":.e0,"e1":.e1,"e2":.e2]))};_testDecodeSuccess(&s, .e1, "{\"key\":\"e1\"}") }
    func testDecode_Enum3() { var s = S<E>(.e0){$0.value .= ($1["key"],EnumCodingConvertor<String,E>(["e0":.e0,"e1":.e1,"e2":.e2]))};_testDecodeSuccess(&s, .e1, "{\"key\":\"1\"}") }
    func testDecode_Enum4() { var s = S<E>(.e0){$0.value .= ($1["key"],EnumCodingConvertor<String,E>(["e0":.e0,"e1":.e1,"e2":.e2]))};_testDecodeSuccess(&s, .e1, "{\"key\":1}") }
    func testDecode_Enum5() { var s = S<E>(.e0){$0.value .= ($1["key"],EnumCodingConvertor<String,E>(["e0":.e0,"e1":.e1,"e2":.e2]))};_testDecodeSuccess(&s, .e0, "{\"key\":10}") }
    func testDecode_Enum6() { var s = S<E>(.e0){$0.value .= ($1["key"],EnumCodingConvertor<String,E>(["e0":.e0,"e1":.e1,"e2":.e2]))};_testDecodeSuccess(&s, .e0, "{\"key\":\"e10\"}") }
    func testDecode_Enum7() { var s = S<E>(nil){$0.value .= ($1["key"],EnumCodingConvertor<String,E>(["e0":.e0,"e1":.e1,"e2":.e2]))};_testDecodeSuccess(&s, nil, "{\"key\":\"e10\"}") }
}
extension BaseTypeTestCase {
    func _testInitSuccess<T>(_ value:T? , _ json:String, _ succ:Bool = true) where T:Codable&Equatable {
        let s = S<T>.mkcodable.decodeFromJSON(json)!
        if succ {
            XCTAssert(s.value == value,"decode \(String(reflecting: T.self)) fail")
        } else {
            XCTAssert(s.value != value,"decode \(String(reflecting: T.self)) should fail")
        }
    }
    func _testInitArraySuccess<T>(_ value:[T?] , _ json:String, _ succ:Bool = true) where T:Codable&Equatable {
        let s = [S<T>].mkcodable.decodeFromJSON(json)!
        let array = s.map{$0.value}
        if succ {
            XCTAssert(array == value,"decode \(String(reflecting: T.self)) fail")
        } else {
            XCTAssert(array != value,"decode \(String(reflecting: T.self)) should fail")
        }
    }
    func _testDecodeSuccess<T>(_ s:inout S<T> ,_ value:T? ,_ json:String, _ succ:Bool = true) where T:Codable&Equatable {
        s.mkcodable.decodeFromJSON(json)
        if succ {
            XCTAssert(s.value == value,"decode \(String(reflecting: T.self)) fail")
        } else{
            XCTAssert(s.value != value,"decode \(String(reflecting: T.self)) should fail")
        }
    }
    func _testDecodeArraySuccess<T>(_ s:inout [S<T>] ,_ value:[T?] ,_ json:String, _ succ:Bool = true) where T:Codable&Equatable {
        s.mkcodable.decodeFromJSON(json)
        let valueDecode = s.map{$0.value}
        if succ {
            XCTAssert(valueDecode == value,"decode \(String(reflecting: T.self)) fail")
        } else{
            XCTAssert(valueDecode != value,"decode \(String(reflecting: T.self)) should fail")
        }
    }
    func _testEncodeSuccess<T>(_ value:T, _ json:String,_ succ:Bool = true) where T:Codable {
        let jsonString = value.mkcodable.encodeToJSONString()!
        if succ {
            XCTAssert(jsonString == json,"encode result is :\(jsonString)")
        } else {
            XCTAssert(jsonString != json,"encode result is :\(jsonString)")
        }
    }
}
